
package cz.czu.pef.DataModelling.beans;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
// import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;

import org.richfaces.component.SortOrder;
import org.richfaces.model.SelectionMode;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import cz.czu.pef.DataModelling.domainObjects.IdObject;
import cz.czu.pef.common.CustomFilterField;
import cz.czu.pef.common.CustomSortField;


@Component
@ViewScoped
@ManagedBean
public abstract class AbstractExtendedTableBean<T extends IdObject> implements Serializable, ApplicationContextAware
{

	private static final long serialVersionUID = -2509946708890778188L;

	private static final Logger logger = Logger.getLogger(AbstractExtendedTableBean.class.getName());

	private String sortMode = "multi";
	private SelectionMode selectionMode = SelectionMode.multiple;
	private Collection<Object> selection;
	private List<T> selectionItems = new ArrayList<T>();
	protected List<T> items = new ArrayList<T>();

	protected PagingExtendedTableDataModel<T> dataModel;

	private Map<String, SortOrder> sortsOrders = new HashMap<String, SortOrder>();
	private List<String> sortPriorities = new ArrayList<String>();

	private Map<String, String> filterValues = new HashMap<String, String>();

	private static final String SORT_PROPERTY_PARAMETER = "sortProperty";

	protected ApplicationContext applicationContext;

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
	{
		this.applicationContext = applicationContext;
	}

	public String getSortMode()
	{
		return sortMode;
	}

	public void setSortMode(String sortMode)
	{
		this.sortMode = sortMode;
	}

	public SelectionMode getSelectionMode()
	{
		return selectionMode;
	}

	public void setSelectionMode(SelectionMode selectionMode)
	{
		this.selectionMode = selectionMode;
	}

	public void filterListener(AjaxBehaviorEvent event)
	{
		filter();
	}

	public void selectionListener(AjaxBehaviorEvent event)
	{
		/*
		UIExtendedDataTable dataTable = (UIExtendedDataTable) event.getComponent();
		Object originalKey = dataTable.getRowKey();
		selectionItems.clear();
		for (Object selectionKey : selection)
		{
			dataTable.setRowKey(selectionKey);
			if (dataTable.isRowAvailable())
			{
				selectionItems.add((T) dataTable.getRowData());
			}
		}
		dataTable.setRowKey(originalKey);
		*/
		takeSelection();
	}

	private void takeSelection()
	{
		if (selectionItems == null)
			selectionItems = new ArrayList<T>();
		selectionItems.clear();

		if (getSelection() == null)
		{
			logger.warning("takeSelection():: getSelection returned null");
			return;
		}

		Iterator<Object> iterator = getSelection().iterator();
		while (iterator.hasNext())
		{
			Object key = iterator.next();
			selectionItems.add(getItemsDataModel().getObjectByKey(key));
		}
	}

	public void setItems(List<T> items)
	{
		this.items = items;
	}

	public Collection<Object> getSelection()
	{
		return selection;
	}

	public void setSelection(Collection<Object> selection)
	{
		this.selection = selection;
	}

	public List<T> getSelectionItems()
	{
		return selectionItems;
	}

	public void setSelectionItems(List<T> selectionItems)
	{
		this.selectionItems = selectionItems;
	}

	public boolean isSelectionItemsEmpty()
	{
		return selectionItems.isEmpty();
	}

	public Map<String, SortOrder> getSortsOrders()
	{
		return sortsOrders;
	}

	public List<String> getSortPriorities()
	{
		return sortPriorities;
	}

	public Map<String, String> getFilterValues()
	{
		return filterValues;
	}

	public void setFilterValues(Map<String, String> filterValues)
	{
		this.filterValues = filterValues;
		dataModel.getDataProvider().setCustomFilterFields(filterValues);
	}

	public void sort()
	{
		try
		{
			logger.finest("sort():: Entry");
			String sortProperty = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(SORT_PROPERTY_PARAMETER);
			if (sortProperty != null)
			{
				logger.fine("sort():: processing sortProperty=" + sortProperty);

				SortOrder currentPropertySortOrder = sortsOrders.get(sortProperty);
				if (!sortPriorities.contains(sortProperty))
				{
					logger.fine("sort():: adding new sortProperty=" + sortProperty);
					sortPriorities.add(sortProperty);
				}

				// toggle: unordered => ascending => descending => unordered
				if (currentPropertySortOrder == null)
				{
					// ascending
					logger.info("sort():: setting sortProperty=" + sortProperty + " to " + SortOrder.ascending);
					sortsOrders.put(sortProperty, SortOrder.ascending);
				}
				else if (SortOrder.ascending.equals(currentPropertySortOrder))
				{
					// descending
					logger.info("sort():: setting sortProperty=" + sortProperty + " to " + SortOrder.descending);
					sortsOrders.put(sortProperty, SortOrder.descending);
				}
				else
				{
					// unordered
					logger.info("sort():: removing sortProperty=" + sortProperty);
					sortsOrders.remove(sortProperty);

					// remove it also from sortPriorities list
					for (int i = sortPriorities.size() - 1; i >= 0; --i)
					{
						if (sortPriorities.get(i).equals(sortProperty))
							sortPriorities.remove(i);
					}
				}
			}

			if (dataModel != null && dataModel.getDataProvider() != null)
			{
				logger.info("sort():: Invoking dataModel.getDataProvider().setCustomSortFields");
				dataModel.getDataProvider().setCustomSortFields(sortsOrders);
				dataModel.cleanDataProviderCache();
			}
		}
		finally
		{
			logger.finest("sort():: Exit");
		}
	}

	public void filter()
	{
		try
		{
			logger.finest("filter():: Entry");

			if (dataModel != null && dataModel.getDataProvider() != null)
			{
				logger.info("filter():: Invoking dataModel.getDataProvider().setCustomFilterFields");
				dataModel.getDataProvider().setCustomFilterFields(filterValues);
				dataModel.cleanDataProviderCache();
			}
		}
		finally
		{
			logger.finest("filter():: Exit");
		}
	}

	public void resetSorts()
	{
		sortPriorities.clear();
		sortsOrders.clear();
	}

	public String removeSelected()
	{
		try
		{
			logger.finest("removeSelected():: Entry");

			if (!isSelectionItemsEmpty())
			{
				Iterator<T> iter = getSelectionItems().iterator();
				while (iter.hasNext())
				{
					T typedObject = iter.next();
					// Object key = iter.next();
					//T typedObject = service.getByStringId(key.toString());
					if (typedObject != null)
					{
						logger.info("removeSelected():: Removing=" + typedObject.toString());

						if (canBeRemoved(typedObject))
							removeById(typedObject.getId());
						else
							logger.warning("removeSelected():: Cannot remove object because of dependencies: " + typedObject);
					}
				}

				setSelection(null);
			}

			return null;
		}
		finally
		{
			logger.finest("removeSelected():: Exit");
		}
	}

	public PagingExtendedTableDataModel<T> getItemsDataModel()
	{
		if (dataModel == null)
		{
			dataModel = new PagingExtendedTableDataModel<T>(new PagingDataProvider<T>()
				{
					private static final long serialVersionUID = 5054087821033164847L;

					public T getItemByKey(Object key)
					{
						String myKey = key.toString();
						logger.finest("getItemByKey():: key=" + myKey);
						return getByStringId(myKey);
					}

					public List<T> getItemsByRange(int firstRow, int endRow)
					{
						logger.finest("getItemsByRange():: firstRow(" + firstRow + "), endRow(" + endRow + ")");
						return findAllByRangeWithFilterAndSort(firstRow, endRow, getCustomFilterFields(), getCustomSortFields());
					}

					public Object getKey(T item)
					{
						if (logger.isLoggable(Level.FINEST))
							logger.finest("getKey():: item=" + item);
						return item.getId();
					}

					public int getRowCount()
					{
						Long count = countWithFilter(getCustomFilterFields());
						logger.finest("getRowCount():: count=" + count);
						if (count != null)
							return count.intValue();
						return 0;
					}

				});
		}
		return dataModel;
	}

    public abstract boolean canBeRemoved(T entity);
    public abstract void removeById(long id);
    public abstract T getByStringId(String myKey);
    public abstract List<T> findAllByRangeWithFilterAndSort(Integer firstRow, Integer endRow, List<CustomFilterField> filterFields, List<CustomSortField> sortFields);
    public abstract Long countWithFilter(List<CustomFilterField> filterFields);
}
